<!DOCTYPE html>
<html>
<head>
<title>Backgammon</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../js/snap.svg-min.js"></script>
<style>
/*
.board-container { 
	display: inline-block;
	position: relative;
	width: 80%;
	padding-bottom: 47%; 
	vertical-align: middle; 
	overflow: hidden; 
}

*/

body {
        margin: 0px;
        box-sizing: border-box;
        max-height: 100vh;
}

.game-ui {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        box-sizing: border-box;
        max-height: 100vh;
        flex-basis: auto;
        padding: 20px;
}

.toolbar {
        display: flex;
        flex-direction: row;
        flex-grow: 2;
        justify-content: space-between;
        align-items: baseline;
}

.toolbar-button {
    flex: 1 1 100px;
    margin: 2px;
    height: 40px;
    width: 50px;
}

.player-info {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
}

.match-info {
    display: flex;
    box-sizing: border-box;
    flex-direction: row;
    justify-content: space-between;
    margin-right: 50px;
}

.svg-board { 
    flex: 1 2 auto;
    padding: 5px;
}

.hint {
    height: 1em;
}

</style>


    
</head>

<body>
    <div class="game-ui">
        <div id="toolbar" class="toolbar" style="padding-bottom: 10px;">
            <!-- <svg id="player" width=20 height=20> </svg> -->
            <span id="player"></span>
            <button id="double" type="button" class="toolbar-button" onclick="graphicToolbar.onDouble()" disabled="true">Double</button>
            <button id="accept" type="button" class="toolbar-button" onclick="graphicToolbar.onAccept()" disabled="true">Accept</button>
            <button id="refuse" type="button" class="toolbar-button" onclick="graphicToolbar.onRefuse()" disabled="true">Refuse</button>
            <button id="undo" type="button" class="toolbar-button" onclick="graphicToolbar.onUndo()" disabled="false">Undo</button>
            <button id="direction" type="button" class="toolbar-button" onclick="graphicToolbar.onChangeDirection()">Direction</button>
        </div>
        <svg id="board" viewBox="0 0 520 300" class="svg-board"></svg>
        <p id="hint" class="hint"></p>
        <div class="match-info">
            <div class="player-info">
                <div class="player-name">White <span id="white-name"></span></div>
                <div class="score">Score: <span id="white-score"></span> <span id="white-away"></span></div>
                <div class="pips">Pips: <span id="white-pips"></span> (<span id="white-diff"></span>)</div>
            </div>
            <div class="player-info">
                <div class="player-name">Black <span id="black-name"></span></div>
                <div class="score">Score: <span id="black-score"></span> <span id="black-away"></span></div>
                <div class="pips">Pips: <span id="black-pips"></span> (<span id="black-diff"></span>)</div>
            </div>
            <div class "match-options">
                <div class="match-limit">
                    Match: <span id="match-limit"></span>
                </div>
                <div class="match-misc">
                    <span id="match-misc"></span>
                </div>
            </div>

        </div>
    </div>
        <div id="status" hidden="true"></div>

<script>

"use strict";

function Board(game, ui, boardPic) {

    this.pic = boardPic;
    this.game = game;
    this.ui = ui;

    var self = this;

    this.clockwise = true;
    this.orientation = "white";

    this.onChangeDirection = function() {
        self.clockwise = !self.clockwise;
        self.refresh();
    }

    this.onPointClick = function(p) {
        var a = physicalToAbsolute(p, self.clockwise, self.orientation);
        if (ui.checkersEnabled) {
            let canMove = game.canMoveFrom(a);
            if (canMove === "swap") game.swapDice();
            if (canMove) ui.putEvent(moveFromEvent(a, game));
        }
    };

    this.onBarClick = function() {
        if (ui.checkersEnabled) {
            if (ui.checkersEnabled) {
                let canMove = game.canMoveFrom("bar");
                if (canMove === "swap") game.swapDice();
                if (canMove) ui.putEvent(moveFromBarEvent(game));
            }
        }
    };

    this.onRightPaneClick = function() {
        if (ui.moveCompletionEnabled) {
            ui.putEvent(finishMoveEvent());
        } else if (ui.offerDoubleEnabled) {
            ui.putEvent(offerDoubleEvent("no"));
        } else if (game.canSwapDice()) {
            game.swapDice();
            self.refresh();
        }
    }

    this.onTrayClick = function(ID) {
        if (ui.doublingEnabled && ID === self.cubeTrayID[PLAYER]()) {
            ui.putEvent(offerDoubleEvent("yes"));
        }
    }

    this.plug = function() {

        var foo = function() {};

        this.pic.onPointClick = this.onPointClick; // phys_i
        this.pic.onBarClick = this.onBarClick; // ()
        this.pic.onRightPaneClick = this.onRightPaneClick; // ()
        this.pic.onTrayClick = this.onTrayClick; // phys_i

        this.setCheckersBar = this.pic.setCheckersBar || foo; // k_white, k_black
        this.setCheckersPoint = this.pic.setCheckersPoint || foo; // phys_i, n, player
        this.setCheckersTray = this.pic.setCheckersTray || foo; // phys_i, n, player

        this.setCubeLeft = this.pic.setCubeLeft || foo; // val
        this.setCubeRight = this.pic.setCubeRight || foo; // val
        this.setCubeLeftPane = this.pic.setCubeLeftPane || foo; // val
        this.setCubeRightPane = this.pic.setCubeRightPane || foo; // val
        this.setCubeTray = this.pic.setCubeTray || foo;  // phys_i, val
        this.hideCube = this.pic.hideCube || foo; // ()

        this.setDiceLeft = this.pic.setDiceLeft || foo; // d1, opacity1, d2, opacity2; opacity: "max", "half", "min"
        this.setDiceRight = this.pic.setDiceRight || foo;
        this.hideDice = this.pic.hideDice || foo; // ()

    }

    this.refresh = function() {

        // refresh checkers on the points
        for (let i = 0; i < 24; ++i) {
            let a = physicalToAbsolute(i, this.clockwise, this.orientation);
            this.setCheckersPoint(i, game.board.points[a].checkers, game.board.points[a].player);
        }

        // refresh checkers on the bar
        this.setCheckersBar(game.board.bar.white, game.board.bar.black);

        this.hideDice();
        this.hideCube();

        // refresh the trays
        var offWhite = this.bearoffTrayID.white();
        var offBlack = this.bearoffTrayID.black();
        var cubeLocation = this.cubeLocation();
        for (let i = 0; i < 4; ++i) {
            if (i === offWhite) {
                this.setCheckersTray(i, game.board.off.white, "white");
            } else if (i === offBlack) {
                this.setCheckersTray(i, game.board.off.black, "black");
            } else if (i === cubeLocation) {
                this.setCubeTray(i, game.cube);
            } else {
                this.setCheckersTray(i, 0, null);
            }
        }

        // Dice go before cube so that we could hide them without hiding the cube
        // It turns out this.hideDice() removes the cube as well.
        // Is this a bug or not?
        if (!game.dice) this.hideDice()
        else if (!game.turn) {
            if (this.orientation === "white") {
                this.setDiceRight(game.dice[0], "max");
                this.setDiceLeft(game.dice[1], "max");
            } else {
                this.setDiceRight(game.dice[1], "max");
                this.setDiceLeft(game.dice[0], "max");
            }
        } else {
            let op0 = "max", op1 = "max";
            if (game.turn === PLAYER) {
                if (game.dice[0] != game.dice[1]) {
                    if (!game.restDice.includes(game.dice[0])) op0 = "min";
                    if (!game.restDice.includes(game.dice[1])) op1 = "min";
                } else {
                    let len = game.restDice.length;
                    if (len === 3) op0 = "half"
                    else if (len === 2) op0 = "min"
                    else if (len === 1) {
                        op0 = "min";
                        op1 = "half";
                    } else if (len === 0) op0 = op1 = "min";
                }
            }

            if (game.turn === this.orientation) this.setDiceRight(game.dice[0], op0, game.dice[1], op1)
            else this.setDiceLeft(game.dice[0], op0, game.dice[1], op1);
        }

        if (cubeLocation === "left") this.setCubeLeft(game.cube)
        else if (cubeLocation === "right") this.setCubeRight(game.cube)
        else if (cubeLocation === "leftPane") this.setCubeLeftPane(2*game.cube)
        else if (cubeLocation === "rightPane") this.setCubeRightPane(2*game.cube)
        else if (cubeLocation === null) this.hideCube(); 


    }

    this.bearoffTrayID = {
        white: function() {
            return (self.orientation === "white") ? (self.clockwise ? 3 : 2) : (self.clockwise ? 0 : 1);
        },
        black: function() {
            return (self.orientation === "black") ? (self.clockwise ? 3 : 2) : (self.clockwise ? 0 : 1);
        }
    }

    // Returns the storage number, "left", "right", or null.
    this.cubeLocation = function() {
        if (!game.cube) return null
        else if (game.isDoubling === PLAYER) return "leftPane"
        else if (game.isDoubling) return "rightPane"
        else if (!game.cubeOwner) {
            if (this.clockwise) return "right"
            else return "left";
        } else if (game.cubeOwner === this.orientation) {
            if (this.clockwise) return 2
            else return 3;
        } else {
            if (this.clockwise) return 1
            else return 0;
        }
    }

    this.cubeTrayID = {
        white: function() {
            return (self.orientation === "white") ? (self.clockwise ? 2 : 3) : (self.clockwise ? 1 : 0);
        },
        black: function() {
            return (self.orientation === "black") ? (self.clockwise ? 2 : 3) : (self.clockwise ? 1 : 0);
        }
    }

}



function normalizeCube(value) {
    return (value === 1) ? 64 : value;
}

function die(pic, x, y, a, r, alpha, value, faceColour, pipColour) {
    var g = pic.g();
    var face = pic.rect(x - 0.5*a, y - 0.5*a, a, a, 5).attr({
        fill: faceColour
    }).appendTo(g);
    var pip = pic.circle(x, y, r).attr({
        fill: pipColour
    }).appendTo(g);

    var shift = alpha*a* 0.5;

    if (value === 1) {
    } else if (value === 2) {
        pip.clone().transform(Snap.matrix().translate(shift, shift));
        pip.transform(Snap.matrix().translate(-shift, -shift));
    } else if (value === 3) {
        pip.clone().transform(Snap.matrix().translate(shift, shift));
        pip.clone().transform(Snap.matrix().translate(-shift, -shift));
    } else if (value === 4) {
        pip.clone().transform(Snap.matrix().translate(shift, shift));
        pip.clone().transform(Snap.matrix().translate(-shift, -shift));
        pip.clone().transform(Snap.matrix().translate(-shift, shift));
        pip.transform(Snap.matrix().translate(shift, -shift));
    } else if (value === 5) {
        pip.clone().transform(Snap.matrix().translate(shift, shift));
        pip.clone().transform(Snap.matrix().translate(-shift, -shift));
        pip.clone().transform(Snap.matrix().translate(-shift, shift));
        pip.clone().transform(Snap.matrix().translate(shift, -shift));
    } else if (value === 6) {
        pip.clone().transform(Snap.matrix().translate(shift, shift));
        pip.clone().transform(Snap.matrix().translate(-shift, -shift));
        pip.clone().transform(Snap.matrix().translate(-shift, shift));
        pip.clone().transform(Snap.matrix().translate(shift, -shift));
        pip.clone().transform(Snap.matrix().translate(shift, 0));
        pip.transform(Snap.matrix().translate(-shift, 0));
    }

    return g;
};

function cube(pic, x, y, a, value, bgColour, fgColour, orientation) {

    a = a * 0.9;
    var g = pic.g();

    pic.rect(x - 0.5*a, y - 0.5*a, a, a, 5).attr({
        fill: bgColour
    }).appendTo(g);

    var label = pic.text(x, y, value).attr({
        textAnchor: "middle",
        alignmentBaseline: "central",
        fill: "beige"
    });

    var t = Snap.matrix();

    if (orientation === "W") t.rotate(-90, x, y)
    else if (orientation === "S")  t.rotate(180, x, y)
    else if (orientation === "E")  t.rotate(90, x, y)

    label.transform(t).appendTo(g);

    return g;
};

function moveFromEvent(p, game) {
    return moveEvent(p + 1, game.restDice[0]);
}

function moveFromBarEvent(game) {
    return moveEvent("bar", game.restDice[0]);
}

function finishMoveEvent() {
    return ["f"];
}

function undoEvent() {
    return ["u"];
}

function offerDoubleEvent(yesNo) {
    return ["offer-double", yesNo];
}

function acceptDoubleEvent(yesNo) {
    return ["accept-double", yesNo];
}


function SVGPoint(board, ID, checkerCount, player) {

    var self = this;

    var g = board.paper.g();

    g.rect(pointX(board, ID) - 1/2*board.params.pointWidth,
        (ID < 12) ? 0 : board.params.height - board.params.pointHeight,
        0.99*board.params.pointWidth, board.params.pointHeight)
        .attr({
            fillOpacity: 0
        })

    pointPic(board, ID).appendTo(g);

    var checkersToDraw = Math.min(checkerCount, maxCirclesOnPoint);
    var label = (checkerCount > maxCirclesOnPoint) ? checkerCount : null;
    var direction = pointDirection(ID);
    var baseX = pointX(board, ID);
    var baseY = pointBaseY(board, ID);

    for (let i = 0; i < checkersToDraw; ++i) {
            checkerPic(board, player, baseX,
                baseY + direction * (2*i+1) * board.params.checkerRadius).appendTo(g);
        }
    if (checkersToDraw > 0 && label) {
        textLabel(board, baseX, baseY + direction * (2 * checkersToDraw - 1) * board.params.checkerRadius, label).appendTo(g);
    }

    g.click(function () { board.onPointClick(ID) });

    return g;

}

function checkerPic(board, player, x, y) {

    return board.paper.circle(x, y, board.params.checkerRadius).attr({
        fill: board.params.checkerColour[player]
    });
}

function textLabel(board, x, y, text) {
    return board.paper.text(x, y, text).attr({
        textAnchor: "middle",
        alignmentBaseline: "central"
    });

};

function SVGBar(board, white, black) {

    var g = board.paper.g();

    // background
    board.paper.rect(7 * board.params.pointWidth + 2 * board.params.borderWidth, 0, board.params.barWidth, board.params.height)
        .attr({
            fill: board.params.borderColour
        }).appendTo(g);

    // TODO text labels
    var setCheckers = function(k, player) {
        var x = 7 * board.params.pointWidth + 2 * board.params.borderWidth + 0.5 * board.params.barWidth;
        var topY = (player == board.orientation) ? 0.33 * board.params.height : 0.67 * board.params.height;
        var dir = (player == board.orientation) ? -1 : 1;
        for (let i = 0; i < k; ++i) {
            checkerPic(board, player, x,
                topY + dir * (2*i+1) * board.params.checkerRadius).appendTo(g);
        }
    };

    setCheckers(white, "white");
    setCheckers(black, "black");
    g.click(board.onBarClick);
    return g;
}


function Pane(board, which, d0, op0, d1, op1) {

    var g = board.paper.g();

    if (which === "right") g.click(board.onRightPaneClick);

    var x0 = (which === "left") ? board.params.pointWidth + 2 * board.params.borderWidth : 7 * board.params.pointWidth + 2 * board.params.borderWidth + board.params.barWidth;

    var y0 = board.params.borderWidth + board.params.pointHeight;

    // background
    board.paper.rect(x0, y0, 
        6 * board.params.pointWidth, board.params.height - 2*board.params.pointHeight)
        .attr({
            fillOpacity: 0
        }).appendTo(g);

    // two dice
    if (d0 && d1) {
        let a = 30;
        let r = 3;
        let alpha = 0.6;
        let faceColour = "red";
        let pipColour = "white";
        let x1 = x0 + 3 * board.params.pointWidth;
        let y1 = 0.5 * board.params.height;
        let delta = 0.75*a;
        let pic0 = die(board.paper, x1 - delta, y1, a, r, alpha, d0, faceColour, pipColour);
        let pic1 = die(board.paper, x1 + delta, y1, a, r, alpha, d1, faceColour, pipColour);
        if (op0 === "min") {
            pic0.attr({
                fillOpacity: 0.33
            });
        } else if (op0 === "half") {
            pic0.attr({
                fillOpacity: 0.67
            });
        }
        if (op1 === "min") {
            pic1.attr({
                fillOpacity: 0.33
            });
        } else if (op1 === "half") {
            pic1.attr({
                fillOpacity: 0.67
            });
        }
        pic0.appendTo(g);
        pic1.appendTo(g);
    }

    // one die
    // I've copied & pasted the above. :blush: I promise to refactor it some day.
    if (d0 && !d1) {
        let a = 30;
        let r = 3;
        let alpha = 0.6;
        let faceColour = "red";
        let pipColour = "white";
        let x1 = x0 + 3 * board.params.pointWidth;
        let y1 = 0.5 * board.params.height;
        let pic = die(board.paper, x1, y1, a, r, alpha, d0, faceColour, pipColour);
        pic.appendTo(g);
    }
    return g;
}

function Tray(board, ID, checkerCount, player) {

    var g = board.paper.g();

    g.click(function() {board.onTrayClick(ID);});

    var x = (ID === 0 || ID === 3) ? board.params.borderWidth : 13*board.params.pointWidth + board.params.barWidth + 3 * board.params.borderWidth;
    var y = (ID === 0 || ID === 1) ? board.params.borderWidth : board.params.height - board.params.borderWidth - board.params.pointHeight;

    // background
    g.rect(x, y, board.params.pointWidth, board.params.pointHeight)
        .attr({
            fill: board.params.backgroundColour
        });

    // checkers
    var checkersToDraw = Math.min(checkerCount, maxCirclesOnPoint);
    var label = (checkerCount > maxCirclesOnPoint) ? checkerCount : null;
    var direction = (ID < 2) ? 1 : -1;
    var baseX = x + 0.5 * board.params.pointWidth;
    var baseY = (ID === 0 || ID === 1) ? board.params.borderWidth : board.params.height - board.params.borderWidth;

    for (let i = 0; i < checkersToDraw; ++i) {
        checkerPic(board, player, baseX,
            baseY + direction * (2*i+1) * board.params.checkerRadius).appendTo(g);
    }
    if (checkersToDraw > 0 && label) {
        textLabel(board, baseX, baseY + direction * (2 * checkersToDraw - 1) * board.params.checkerRadius, label).appendTo(g);
    }

    return g;
}

function SVGBoard(paper) {

    this.params = {
        width: 520,
        height: 300,
        pointWidth: 30,
        pointHeight: 120,
        barWidth: 60,
        blackCheckerColour: "#753421",
        checkerColour: {
            black: "#753421",
            white: "plum"
        },
        checkerRadius: 10,
        maxCheckersOnPoint: 6,
        evenColour: "#669900",
        oddColour: "#6699ff",
        borderColour: "#ffcc00",
        borderWidth: 10,
        backgroundColour: "beige"
    };

    var self = this;

    this.paper = paper;

    this.onBarClick = function() {};
    this.onPointClick = function() {};
    this.onTrayClick = function() {};
    this.onRightPaneClick = function() {};

    // draw the board
    // TODO check that we don't draw the same things twice
    paper.rect(0, 0, 4 * this.params.borderWidth + 14 * this.params.pointWidth + this.params.barWidth,
        this.params.height).attr({
            fill: this.params.backgroundColour
        });

    paper.rect(0, 0, 2 * this.params.borderWidth + this.params.pointWidth,
        this.params.height).attr({
            fill: this.params.borderColour
        });

    paper.rect(13 * this.params.pointWidth + 2 * this.params.borderWidth + this.params.barWidth, 0, 2 * this.params.borderWidth + this.params.pointWidth,
        this.params.height).attr({
            fill: this.params.borderColour
        });

    paper.rect(0, 0, 14 * this.params.pointWidth + this.params.barWidth + 4 * this.params.borderWidth, this.params.borderWidth).attr({
            fill: this.params.borderColour
        });

    paper.rect(0, this.params.height - this.params.borderWidth, 14 * this.params.pointWidth + this.params.barWidth + 4 * this.params.borderWidth, this.params.borderWidth).attr({
            fill: this.params.borderColour
        });

    this.point = function(x, y, direction, colour) {
        var triangle = paper.polyline([x, y, x + this.params.pointWidth, y, x + 0.5 * this.params.pointWidth, y + direction * this.params.pointHeight, x, y]);
        triangle.attr({
            fill: colour
        });
        return triangle;
    }

    // draw the points, etc.
    var bar = SVGBar(this, 0, 0);

    var trays = [];
    for (let i = 0; i < 4; ++i) trays.push(Tray(this, i, 0, null));

    var cubePic = null;

    var points = [];
    for (let i = 0; i < 24; ++i) points.push(SVGPoint(this, i, 0, null));

    var leftPane = null;
    var rightPane = null;

    this.setCubeLeft = function(c) {
        var x = self.params.borderWidth + 0.5 * self.params.pointWidth;
        var y = 0.5 * self.params.height;
        if (cubePic) cubePic.remove();
        cubePic = cube(paper, x, y, self.params.pointWidth,
            normalizeCube(c), "brown", "beige", "W");
    }

    this.setCubeRight = function(c) {
        var x = 3 * self.params.borderWidth + 13.5 * self.params.pointWidth + self.params.barWidth;
        var y = 1/2 * self.params.height;
        if (cubePic) cubePic.remove();
        cubePic = cube(paper, x, y, self.params.pointWidth,
            normalizeCube(c), "brown", "beige", "W");
    }

    this.setCubeRightPane = function(value) {
        var x = 2 * self.params.borderWidth + 7 * self.params.pointWidth + self.params.barWidth + 3 * self.params.pointWidth;
        var y = 0.5 * self.params.height;
        if (rightPane) rightPane.remove();
        rightPane = Pane(self, "right");
        if (cubePic) cubePic.remove();
        cubePic = cube(paper, x, y, self.params.pointWidth,
            normalizeCube(value), "brown", "beige", "N");
        cubePic.appendTo(rightPane);
    }

    this.setCubeLeftPane = function(value) {
        var x = 2 * self.params.borderWidth + 4 * self.params.pointWidth;
        var y = 0.5 * self.params.height;
        if (leftPane) leftPane.remove();
        leftPane = Pane(self, "left");
        if (cubePic) cubePic.remove();
        cubePic = cube(paper, x, y, self.params.pointWidth,
            normalizeCube(value), "brown", "beige", "N");
        cubePic.appendTo(leftPane);
    }

    this.hideCube = function() {
        if (cubePic) cubePic.remove();
    }

    this.setCubeTray = function(ID, value) {
        var baseX = (ID === 0 || ID === 3) ? self.params.borderWidth + 1/2 * self.params.pointWidth :
            13.5 * self.params.pointWidth + 3 * self.params.borderWidth + self.params.barWidth;
        var baseY = (ID === 0 || ID === 1) ? self.params.borderWidth :
            self.params.height - self.params.borderWidth;
        var direction = (ID === 0 || ID === 1) ? 1 : -1;

        if (cubePic) cubePic.remove();
        cubePic = cube(paper, baseX, baseY + direction * 0.5 * self.params.pointWidth, self.params.pointWidth,
                normalizeCube(value), "brown", "beige", (ID < 2) ? "S" : "N");

    }

    this.bar = SVGBar(this);
    this.leftPane = Pane(this, "left");
    this.rightPane = Pane(this, "right");

    this.setDiceLeft = function(d0, op0, d1, op1) {
        if (leftPane) leftPane.remove();
        leftPane = Pane(self, "left", d0, op0, d1, op1);
    }

    this.setDiceRight = function(d0, op0, d1, op1) {
        if (rightPane) rightPane.remove();
        rightPane = Pane(self, "right", d0, op0, d1, op1);
    }

    this.hideDice = function() {
        if (leftPane) leftPane.remove();
        leftPane = Pane(self, "left");
        if (rightPane) rightPane.remove();
        rightPane = Pane(self, "right");
    }

    this.setCheckersPoint = function(id, k, player) {
        if (points[id]) points[id].remove();
        points[id] = SVGPoint(self, id, k, player);
    }

    this.setCheckersBar = function(white, black) {
        if (bar) bar.remove();
        bar = SVGBar(self, white, black);
    }

    this.setCheckersTray = function(id, k, player) {
        if (trays[id]) trays[id].remove();
        trays[id] = Tray(self, id, k, player);
    }

}

function Game() {

    this.dice = null;
    this.restDice = null;
    this.turn = null;
    this.moves = null;
    this.diceNo = -1;
    this.gameNo = -1;

    this.canMoveFrom = function(p) {
        var restDice = this.restDice;
        var from = (p === "bar") ? "bar" : p + 1;
        if (restDice.length === 0) return false;
        if (this.moves.some(function(move) {
            return (move[0] === restDice[0] && move[1] === from);
        })) {
            return "no-swap";
        }
        if (restDice.length === 1) return "swap";
        if (this.moves.some(function(move) {
            return (move[0] === restDice[1] && move[1] === from);
        })) {
            return "swap";
        }
        return false;
    }

    this.canSwapDice = function() {
        var dice = this.restDice;
        return (game.turn === PLAYER) && (dice.length == 2) && (dice[0] != dice[1]);
    }

    this.swapDice = function() {
        var tmp = this.restDice[0];
        this.restDice[0] = this.restDice[1];
        this.restDice[1] = tmp;
        tmp = this.dice[0];
        this.dice[0] = this.dice[1];
        this.dice[1] = tmp;
    }

    this.pips = function(player) {
        var pips = 0;
        var points = this.board.points;
        for (let i = 0; i < 24; ++i) {
            if (points[i].player === player) {
                pips += points[i].checkers * ((player === "white") ? (i + 1) : (24 - i));
            }
        }
        pips += this.board.bar[player] * 25;
        return pips;
    }


}

function UI() {

    this.putEvent = function(evt) {
        sendEvent(evt);
    } ;

}


function Toolbar(game, ui, board, graphicToolbar) {

    this.pic = graphicToolbar;
    this.game = game;
    this.ui = ui;
    this.board = board;

    var self = this;

    this.plug = function() {

        var foo = function() {};

        this.pic.onDouble = this.onDouble;
        this.pic.onAccept = this.onAccept;
        this.pic.onRefuse = this.onRefuse;
        this.pic.onUndo = this.onUndo;
        this.pic.onChangeDirection = this.board.onChangeDirection;

        this.enableDouble = this.pic.enableDouble || foo;
        this.enableAccept = this.pic.enableAccept || foo;
        this.enableRefuse = this.pic.enableRefuse || foo;
        this.enableUndo = this.pic.enableUndo || foo;

    }


    // TODO We assume that disabling works properly, but still it would be nice
    // to check ui & make sure we are allowed to do that.
    this.onDouble = function() {
        ui.putEvent(offerDoubleEvent("yes"));
    }

    this.onAccept = function() {
        ui.putEvent(acceptDoubleEvent("yes"));
    }

    this.onRefuse = function() {
        ui.putEvent(acceptDoubleEvent("no"));
    }

    this.onUndo = function() {
        if (ui.undoEnabled) ui.putEvent(undoEvent());
    }

    this.refresh = function() {
        this.enableDouble(ui.offerDoubleEnabled);
        this.enableAccept(ui.acceptDoubleEnabled);
        this.enableRefuse(ui.acceptDoubleEnabled);
        this.enableUndo(ui.undoEnabled);
    }

}

function UserInterface(game, ui, graphicBoard, graphicToolbar, matchInfo, graphicHint) {
    this.game = game;
    this.ui = ui;
    this.board = new Board(game, ui, graphicBoard);
    this.toolbar = new Toolbar(game, ui, this.board, graphicToolbar);
    this.matchinfo = new MatchInfo(game, matchInfo);
    this.hint = new Hint(graphicHint);

    this.board.plug();
    this.toolbar.plug();
    this.matchinfo.plug();
    this.hint.plug()

    this.refresh = function() {
        this.board.refresh();
        this.toolbar.refresh();
        this.matchinfo.refresh();

        if (game.matchLimit && game.matchLimit <= game.matchScore[0]) {
            this.hint.setHint("White wins the match.");
        } else if (game.matchLimit && game.matchLimit <= game.matchScore[1]) {
            this.hint.setHint("Black wins the match.");
        } else if (game.winner) {
            this.hint.setHint(game.winner + " wins the game.");
        } else if (!game.turn) {
            this.hint.setHint("Starting the game.");
        } else if (ui.checkersEnabled) {
            this.hint.setHint("Move your checkers and click on the right to complete the move.");
        } else if (ui.offerDoubleEnabled) {
            this.hint.setHint("Double or click on the right to roll the dice.");
        } else if (ui.acceptDoubleEnabled) {
            this.hint.setHint("Accept or refuse the double.");
        } else if (game.turn === PLAYER && game.dice && ! game.moves) {
            this.hint.setHint("NO MOVES");
        } else if (game.turn !== PLAYER) {
            this.hint.setHint("Your opponent’s turn.");
        } else {
            this.hint.setHint(null);
        }
    }
}

function GraphicToolbar() {

    var self = this;

    this.doubleButton = document.getElementById("double")
    this.acceptButton = document.getElementById("accept")
    this.refuseButton = document.getElementById("refuse")
    this.undoButton = document.getElementById("undo");
    this.directionButton = document.getElementById("direction")

    this.enableDouble = function(b) {
        self.doubleButton.disabled = !b;
    }

    this.enableAccept = function(b) {
        self.acceptButton.disabled = !b;
    }

    this.enableRefuse = function(b) {
        self.refuseButton.disabled = !b;
    }

    this.enableUndo = function(b) {
        self.undoButton.disabled = !b;
    }

}

function MatchInfo(game, graphicMatchInfo) {

    this.pic = graphicMatchInfo;
    this.plug = function() {

        var foo = function() {};

        this.setNames = this.pic.setNames || foo; // white-name black-name
        this.setScore = this.pic.setScore || foo; // player score away
        this.setPips = this.pic.setPips || foo; // player pips diff
        this.setMatchLimit = this.pic.setMatchLimit || foo; // limit/null
        this.setCrawford = this.pic.setCrawford || foo; // bool
        this.setJacoby = this.pic.setJacoby || foo; // bool

    }

    this.refresh = function() {
        // TODO names
        //this.setName("white", game.names.white);
        this.setNames(ui.whiteName, ui.blackName);
        this.setScore("white", game.matchScore[0], game.matchLimit && (game.matchLimit - game.matchScore[0]));
        this.setScore("black", game.matchScore[1], game.matchLimit && (game.matchLimit - game.matchScore[1]));

        var whitePips = game.pips("white");
        var blackPips = game.pips("black");
        this.setPips("white", whitePips, whitePips - blackPips);
        this.setPips("black", blackPips, blackPips - whitePips);
        this.setMatchLimit(game.matchLimit);
        if (game.matchLimit) {
            this.setCrawford(game.isCrawford);
        } else {
            this.setJacoby(game.withJacoby);
        }
    }
}

function GraphicMatchInfo() {

    this.setNames = function(whiteName, blackName) {
        document.getElementById("white-name").innerHTML = whiteName || "";
        document.getElementById("black-name").innerHTML = blackName || "";
    }

    this.setScore = function(player, score, away) {
        document.getElementById( player === "white" ? "white-score" : "black-score").innerHTML = score;
        if (!isNaN(away)) {
            if (away > 0) {
                document.getElementById( player === "white" ? "white-away" : "black-away")
                    .innerHTML = "(" + away + "-away)";
            } else {
                document.getElementById( player === "white" ? "white-away" : "black-away")
                    .innerHTML = "(won match)";
            }
        }
    }

    this.setPips = function(player, pips, diff) {
        document.getElementById( player === "white" ? "white-pips" : "black-pips").innerHTML = pips;
        document.getElementById( player === "white" ? "white-diff" : "black-diff").innerHTML
            = (diff >= 0) ? "+" + diff : diff;

    }

    this.setMatchLimit = function(limit) {
        document.getElementById("match-limit").innerHTML = (limit || "unlimited");
    }

    this.setCrawford = function(isCrawford) {
        document.getElementById("match-misc").innerHTML = isCrawford ? "Crawford game" : "";
    }

    this.setJacoby = function(withJacoby) {
        document.getElementById("match-misc").innerHTML = withJacoby ? "Jacoby rule" : "";
    }

}

function Hint(graphicHint) {

    this.pic = graphicHint;

    this.plug = function () {
        // set to null to clear
        this.setHint = this.pic.setHint;
    }
}

function GraphicHint() {

    var self = this;

    this.setHint = function(text) {
        document.getElementById("hint").innerHTML = text || "";
    }
}



var score = [0, 0];
var game = new Game();
var ui = new UI();

//var boardPic = Snap(520, 300);
var boardPaper = Snap("#board");
var boardPic = new SVGBoard(boardPaper);
var maxCirclesOnPoint = 6;

//var board = new Board(game, ui, boardPic)

var graphicToolbar = new GraphicToolbar();
var userIntereface = new UserInterface(game, ui, boardPic, graphicToolbar, new GraphicMatchInfo(), new GraphicHint());

var PLAYER = "watching";
document.getElementById("player").innerHTML = PLAYER;



function statusBar() {
    return document.getElementById("status");
}

function point(x, y, direction, colour) {
    var triangle = boardPic.polyline([x, y, x + board.pointWidth, y, x + 0.5 * board.pointWidth, y + direction * board.pointHeight, x, y]);
    triangle.attr({
        fill: colour
    });
    return triangle;
}

function checkPointID(ID) {
    if (!(0 <= ID && ID < 24))
        throw "Invalid point ID";
}

function checkPlayer(player) {
    if (!(player == "black" || player == "white" || player === null))
        throw "Invalid player";
}

function pointPic(board, ID) {
    checkPointID(ID);
    var direction = pointDirection(ID);
    var colour = (ID % 2 == 0) ? board.params.evenColour : board.params.oddColour;
    return board.point(pointX(board, ID) - 0.5 * board.params.pointWidth, pointBaseY(board, ID), direction, colour);
}

function pointX(board, ID) {
    checkPointID(ID);
    var base = (ID < 12) ? ID * board.params.pointWidth : (23 - ID) * board.params.pointWidth;
    base += 0.5 * board.params.pointWidth + 2 * board.params.borderWidth + board.params.pointWidth;
    if (6 <= ID && ID < 18) base += board.params.barWidth;
    return base;
}

function pointBaseY(board, ID) {
    return (ID < 12) ? board.params.borderWidth : board.params.height - board.params.borderWidth;
}

function pointTopY(board, ID) {
    checkPointID(ID);
    return (ID < 12) ? board.params.pointHeight : board.params.height - board.params.pointHeight;
}

function pointDirection(ID) {
    checkPointID(ID);
    return (ID < 12) ? 1 : -1;
}


function moveEvent(from, pips) {
    return ["m", from, pips];
}

function sendEvent(evt) {
    console.log("sending " + JSON.stringify(evt));
    websocket.send(JSON.stringify(evt));
    statusBar().innerHTML = (JSON.stringify(evt));
}

function continueEvent(decision) {
    return ["c", decision ? "yes" : "no"];
}

function proposalDecisionEvent(decision) {
    return ["proposal-decision", decision ? "yes" : "no"];
}

function dispatchCommand(command) {
    console.log(command);
    if (command[0] == "player") {
        PLAYER = command[1];
        userIntereface.board.orientation = PLAYER;
        document.getElementById("player").innerHTML = PLAYER;
        //checkerPic(Snap("#player"), PLAYER, 10, 10);
    } else if (command[0] == "update") {
        setGame(game, ui, command[1]);
        userIntereface.refresh();
    } else if (command[0] == "continue?") {
        sendEvent(continueEvent(confirm("One more?")));
    } else if (command[0] == "message") {
        statusBar().innerHTML = command[1];
    } else if (command[0] == "proposal") {
        ui.putEvent(proposalDecisionEvent(confirm("Do you want to play with " + command[1] + "?")));
    } else if (command[0] == "cancelled") {
        alert("Cancelled by " + command[1]);
    }

}

function physicalToAbsolute(p, clockwise, orientation) {
    if (clockwise && (orientation === "black")) return p;
    if (!clockwise && (orientation === "black")) return (p < 12) ? 11 - p : 35 - p;
    if (clockwise && (orientation === "white")) return 23 - p;
    if (!clockwise && (orientation === "white")) return (p < 12) ? p + 12 : p - 12;}


function setGame(game, ui,  obj) {
    game.turn = obj.game.turn;
    if (!game.turn) {
        game.dice = obj.game["dice"];
    } else if (obj.game["dice"].length === 0 || obj.game["dice-no"] !== game.diceNo ||
                obj.game["game-no"] !== game.gameNo) {
        game.diceNo = obj.game["dice-no"];
        game.dice = obj.game.dice.sort().reverse();
        game.restDice = obj.game["rest-dice"].sort().reverse();
    } else {
        /* If the move is the same, we've got two dice and we receive two dice
         * from the server, they must be the same two dice as we have, so we
         * copy game.dice to game.restDice in order to save the custom dice
         * order.  Otherwise we either have a single die or identical dice,
         * either way we don't care about the order.
         */
        game.restDice = (game.dice.length === 2 && obj.game["rest-dice"].length === 2)
            ? game.dice.slice() : obj.game["rest-dice"]
    }
    game.gameNo = obj.game["game-no"];
    game.cube = obj.game["cube"];
    game.cubeOwner = obj.game["cube-owner"];
    game.isDoubling = obj.game["is-doubling"];
    game.isCrawford = obj.game["crawford?"];
    game.withJacoby = obj.game["jacoby?"];
    game.matchLimit = obj.game["match-limit"];
    game.result = obj.game.result;
    game.moves = obj.game.moves;
    game.winner = obj.game["winner"];
    game.score = obj.game["score"];
    game.matchScore = obj.game["match-score"];
    game.board = obj.game.board;

    if (obj.ui) {
        ui.checkersEnabled = obj.ui["checkers-enabled"];
        ui.undoEnabled = obj.ui["undo-enabled"];
        ui.moveCompletionEnabled = obj.ui["move-completion-enabled"];
        ui.offerDoubleEnabled = obj.ui["offer-double-enabled"];
        ui.acceptDoubleEnabled = obj.ui["accept-double-enabled"];
        ui.whiteName = obj.ui["white-name"];
        ui.blackName = obj.ui["black-name"];
    }

}



  var wsUri = "ws://" + window.location.host + window.location.pathname;
  var websocket = new WebSocket(wsUri);

  function setupWebSocket()
  {
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onclose = function(evt) { onClose(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
  }

  function onOpen(evt)
  {
      statusBar().innerHTML = "connected";
  }

  function onClose(evt)
  {
      statusBar().innerHTML = "disconnected";
  }

  function onMessage(evt)
  {
      //setGameFromJSON(board, evt.data.toLowerCase());
      dispatchCommand(JSON.parse(evt.data.toLowerCase()));
      //alert(evt.data.toLowerCase());
  }

  function onError(evt)
  {
  }

  function doSend(message)
  {
  }

  function writeToScreen(message)
  {
  }

  //window.addEventListener("load", setupWebSocket, false);
setupWebSocket();

//console.log(new checkerPic("black", pointX(board, 3), pointTopY(board, 3) - board.checkerRadius, "15"));

//var st = new SVGStorage(board, 1);
//st.setCheckers(4, "white");
//
/*
{

        this.points.forEach(function(p, i) {
            let a = physicalToAbsolute(i, board.clockwise, board.orientation);
            p.setCheckers(game.board.points[a].checkers, game.board.points[a].player);
        });

        this.bar.setCheckers(game.board.bar.white, "white");
        this.bar.setCheckers(game.board.bar.black, "black");
        this.undoButton.disabled = !ui.undoEnabled;
        this.leftPane.refresh();
        document.getElementById("accept").disabled = !ui.acceptDoubleEnabled;
        document.getElementById("refuse").disabled = !ui.acceptDoubleEnabled;
        document.getElementById("double").disabled = !ui.offerDoubleEnabled;

        var offWhite = this.bearoffStorageID.white();
        var offBlack = this.bearoffStorageID.black();
        var cubeLocation = this.cubeLocation();
        for (let i = 0; i < 4; ++i) {
            if (i === offWhite) {
                this.storages[i].setCheckers(game.board.off.white, "white");
            } else if (i === offBlack) {
                this.storages[i].setCheckers(game.board.off.black, "black");
            } else if (i === cubeLocation) {
                this.storages[i].setCube(game.cube);
            } else {
                this.storages[i].setCheckers(0, null);
            }
        }

        if (cubeLocation === "left") this.setCubeLeft(game.cube)
        else if (cubeLocation === "right") this.setCubeRight(game.cube)
        else if (cubeLocation === "leftPane") this.setCubeLeftPane(2*game.cube);
        else if (cubeLocation === "rightPane") this.setCubeRightPane(2*game.cube)
        else if (cubeLocation === null && this.cubePic) this.cubePic.remove();

        document.getElementById("pips").innerHTML = "Pips: white " + game.pips("white") + " black " + game.pips("black");

        if (!game.winner) statusBar().innerHTML = game.turn + " " + game.restDice;
        else {
            let winner = game.winner;
            let res = game.score;
            let text = (res === 1) ? winner + " wins 1 point." : winner +  " wins " + res + " points."
            alert(text);
            statusBar().innerHTML = text;
        }

    }

*/
                         function showStatus() {
                         document.getElementById("status").hidden = false;
                         }
</script>



</body>

